#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <aio.h>
#include <signal.h>
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include "boost/filesystem.hpp"
#include "boost/filesystem/fstream.hpp"
#define BOOST_FILESYSTEM_NO_DEPRECATED

#define BUF_SIZE 20     /* Size of buffers for read operations */

#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

#define errMsg(msg)  do { perror(msg); } while (0)

struct ioRequest {      /* Application-defined structure for tracking
                          I/O requests */
   int           reqNum;
   int           status;
   struct aiocb *aiocbp;
};

static volatile sig_atomic_t gotSIGQUIT = 0;
                       /* On delivery of SIGQUIT, we attempt to
                          cancel all outstanding I/O requests */

static void             /* Handler for SIGQUIT */
quitHandler(int sig)
{
   gotSIGQUIT = 1;
}

#define IO_SIGNAL SIGUSR1   /* Signal used to notify I/O completion */

static void                 /* Handler for I/O completion signal */
aioSigHandler(int sig, siginfo_t *si, void *ucontext)
{
 //  write(STDOUT_FILENO, "I/O completion signal received\n", 31);

   /* The corresponding ioRequest structure would be available as
          struct ioRequest *ioReq = si->si_value.sival_ptr;
      and the file descriptor would then be available via
          ioReq->aiocbp->aio_fildes */
}

namespace fs = boost::filesystem;

int
main(int argc, char *argv[])
{
    fs::fstream file;
    unsigned numReqs = 0;
    std::vector<std::string> files;
    unsigned long file_count = 0;
    fs::path full_path( fs::initial_path<fs::path>() );

    if (argc > 1)
        full_path = fs::system_complete( fs::path( argv[1] ) );
    else
        std::cout << "\nusage:   sio [path]" << std::endl;



    if (!fs::exists( full_path )) {
        std::cout << "\nNot found: " << full_path.file_string() << std::endl;
        return 1;
    }

    if (fs::is_directory( full_path )) {
     //   std::cout << "\nIn directory: " << full_path.directory_string() << "\n\n";
        fs::directory_iterator end_iter;
        for ( fs::directory_iterator dir_itr(full_path); dir_itr != end_iter; ++dir_itr) {
            try {
                if (fs::is_regular_file( dir_itr->status())) {
                    files.push_back(std::string("files/" + dir_itr->path().filename()));
               //     printf("%s\r\n", files[numReqs].c_str());
                    ++numReqs;
                  //  std::cout << dir_itr->path().filename() << std::endl;
                }


            } catch ( const std::exception & ex ) {
                std::cout << dir_itr->path().filename() << " " << ex.what() << std::endl;
            }
        }
    }








   struct ioRequest *ioList;
   struct aiocb *aiocbList;
   struct sigaction sa;
   int s, j;
   int openReqs;       /* Number of I/O requests still in progress */

   if (argc < 2) {
       fprintf(stderr, "Usage: %s <pathname> <pathname>...\n",
               argv[0]);
       exit(EXIT_FAILURE);
   }

//   numReqs = argc - 1;

    ioList = new ioRequest[numReqs];
    aiocbList = new aiocb[numReqs];

   /* Establish handlers for SIGQUIT and the I/O completion signal */

   sa.sa_flags = SA_RESTART;
   sigemptyset(&sa.sa_mask);

   sa.sa_handler = quitHandler;
   if (sigaction(SIGQUIT, &sa, NULL) == -1)
       errExit("sigaction");

   sa.sa_flags = SA_RESTART | SA_SIGINFO;
   sa.sa_sigaction = aioSigHandler;
   if (sigaction(IO_SIGNAL, &sa, NULL) == -1)
       errExit("sigaction");

   /* Open each file specified on the command line, and queue
      a read request on the resulting file descriptor */
  //  std::cout << "aio requests, numReqs " << numReqs << std::endl;
   for (j = 0; j < numReqs; j++) {
     //  std::cout << "req: " << j << std::endl;
       ioList[j].reqNum = j;
       ioList[j].status = EINPROGRESS;
       ioList[j].aiocbp = &aiocbList[j];

       ioList[j].aiocbp->aio_fildes = open(files[j].c_str(), O_RDONLY);
       if (ioList[j].aiocbp->aio_fildes == -1)
           errExit("open");
    //   printf("opened %s on descriptor %d\n", files[j].c_str(),
//               ioList[j].aiocbp->aio_fildes);

       ioList[j].aiocbp->aio_buf = malloc(BUF_SIZE);
       if (ioList[j].aiocbp->aio_buf == NULL)
           errExit("malloc");

       ioList[j].aiocbp->aio_nbytes = BUF_SIZE;
       ioList[j].aiocbp->aio_reqprio = 0;
       ioList[j].aiocbp->aio_offset = 0;
       ioList[j].aiocbp->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
       ioList[j].aiocbp->aio_sigevent.sigev_signo = IO_SIGNAL;
       ioList[j].aiocbp->aio_sigevent.sigev_value.sival_ptr =
                               &ioList[j];

       s = aio_read(ioList[j].aiocbp);
       if (s == -1)
           errExit("aio_read");
   }

   openReqs = numReqs;

   /* Loop, monitoring status of I/O requests */

   while (openReqs > 0) {
    //   sleep(3);       /* Delay between each monitoring step */

       if (gotSIGQUIT) {

           /* On receipt of SIGQUIT, attempt to cancel each of the
              outstanding I/O requests, and display status returned
              from the cancellation requests */

           printf("got SIGQUIT; canceling I/O requests: \n");

           for (j = 0; j < numReqs; j++) {
               if (ioList[j].status == EINPROGRESS) {
             //      printf("    Request %d on descriptor %d:", j,
//                           ioList[j].aiocbp->aio_fildes);
                   s = aio_cancel(ioList[j].aiocbp->aio_fildes,
                           ioList[j].aiocbp);
                   if (s == AIO_CANCELED)
                       printf("I/O canceled\n");
                   else if (s == AIO_NOTCANCELED)
                           printf("I/O not canceled\n");
                   else if (s == AIO_ALLDONE)
                       printf("I/O all done\n");
                   else
                       errMsg("aio_cancel");
               }
           }

           gotSIGQUIT = 0;
       }

       /* Check the status of each I/O request that is still
          in progress */

       printf("aio_error( ):\n");
       for (j = 0; j < numReqs; j++) {
           if (ioList[j].status == EINPROGRESS) {
        //       printf("    for request %d (descriptor %d): ",
             //         j, ioList[j].aiocbp->aio_fildes);
               ioList[j].status = aio_error(ioList[j].aiocbp);

               switch (ioList[j].status) {
               case 0:
              //     printf("I/O succeeded\n");
                   break;
               case EINPROGRESS:
              //     printf("In progress\n");
                   break;
               case ECANCELED:
              //     printf("Canceled\n");
                   break;
               default:
               //    errMsg("aio_error");
                   break;
               }

               if (ioList[j].status != EINPROGRESS)
                   openReqs--;
           }
       }
   }

    printf("All I/O requests completed\n");

    /* Check status return of all I/O requests */

    printf("aio_return():\n");
    for (j = 0; j < numReqs; j++) {
       ssize_t s;

       s = aio_return(ioList[j].aiocbp);
    //   printf("    for request %d (descriptor %d): %ld\n",
    //           j, ioList[j].aiocbp->aio_fildes, (long) s);
    }
    delete[] ioList;
    delete[] aiocbList;
    exit(EXIT_SUCCESS);
}
